package logger

import (
	"net"
	"net/http"
	"os"
	"runtime/debug"
	"strings"
	"time"
	"web01/conf"

	"github.com/gin-gonic/gin"
	rotatelogs "github.com/lestrrat/go-file-rotatelogs"
	log "github.com/sirupsen/logrus"
)

var ginLogger *log.Logger

// gin 日志中间件
//	使用 info 级别的 logrus 输出日志
func LogerMiddleware() gin.HandlerFunc {
	// 实例化
	ginLogger = log.New()
	// 设置日志级别
	ginLogger.SetLevel(log.InfoLevel)
	// 日志采用 json 格式
	ginLogger.SetFormatter(&log.JSONFormatter{TimestampFormat: "2006-01-02 15:04:05"})

	// 前台启动，日志也直接输出
	if !*conf.EnableLogFile {
		ginLogger.SetOutput(os.Stdout)
	} else {
		// 日志打卷
		writer, err := rotatelogs.New(
			conf.GinLogFile+".%Y%m%d",
			rotatelogs.WithLinkName(conf.GinLogFile),     // 生成软链，指向最新日志文件
			rotatelogs.WithMaxAge(-1),                    // 文件最大保存时间
			rotatelogs.WithRotationTime(24*time.Hour),    // 日志切割时间间隔
			rotatelogs.WithRotationCount(conf.LogRotate), // 日志文件保留数量
		)
		if err != nil {
			defLogger.Errorf("Init logger error %v", err)
			os.Exit(1)
		}
		ginLogger.SetOutput(writer)
	}

	return func(c *gin.Context) {
		//开始时间
		startTime := time.Now()
		//处理请求
		c.Next()
		//结束时间
		endTime := time.Now()
		// 执行时间
		latencyTime := endTime.Sub(startTime)
		//请求方式
		reqMethod := c.Request.Method
		//请求路由
		reqUrl := c.Request.RequestURI
		//状态码，500 时不输出日志（GinRecovery 会输出）
		statusCode := c.Writer.Status()
		if statusCode == http.StatusInternalServerError {
			return
		}
		//请求 ip，考虑被 nginx 代理的情况
		clientIP := c.GetHeader("X-Real-IP")
		if clientIP == "" {
			clientIP = c.ClientIP()
		}

		// 日志格式
		ginLogger.WithFields(log.Fields{
			"status_code":  statusCode,
			"latency_time": latencyTime,
			"client_ip":    clientIP,
			"req_method":   reqMethod,
			"req_uri":      reqUrl,
		}).Info()

	}
}

// gin 错误恢复中间件
//	当程序 painc 时，返回状态码 500 并日志输出相关内容
func GinRecovery(stack bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace.
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}
				}

				// httpRequest, _ := httputil.DumpRequest(c.Request, false)

				//请求方式
				reqMethod := c.Request.Method
				//请求路由
				reqUrl := c.Request.RequestURI
				//请求 ip，考虑被 nginx 代理的情况
				clientIP := c.GetHeader("X-Real-IP")
				if clientIP == "" {
					clientIP = c.ClientIP()
				}

				if brokenPipe {
					// logger.Error(c.Request.URL.Path,
					// 	zap.Any("error", err),
					// 	zap.String("request", string(httpRequest)),
					// )

					ginLogger.WithFields(log.Fields{
						"status_code": http.StatusInternalServerError,
						"client_ip":   clientIP,
						"req_method":  reqMethod,
						"req_uri":     reqUrl,
					}).Error(err)
					// If the connection is dead, we can't write a status to it.
					c.Error(err.(error)) // nolint: errcheck
					c.Abort()
					return
				}

				if stack {
					// logger.Error("[Recovery from panic]",
					// 	zap.Any("error", err),
					// 	zap.String("request", string(httpRequest)),
					// 	zap.String("stack", string(debug.Stack())),
					// )

					ginLogger.WithFields(log.Fields{
						"status_code": http.StatusInternalServerError,
						"client_ip":   clientIP,
						"req_method":  reqMethod,
						"req_uri":     reqUrl,
						"stack":       string(debug.Stack()),
					}).Error(err)
				} else {
					// logger.Error("[Recovery from panic]",
					// 	zap.Any("error", err),
					// 	zap.String("request", string(httpRequest)),
					// )

					ginLogger.WithFields(log.Fields{
						"status_code": http.StatusInternalServerError,
						"client_ip":   clientIP,
						"req_method":  reqMethod,
						"req_uri":     reqUrl,
					}).Error(err)
				}
				c.AbortWithStatus(http.StatusInternalServerError)
			}
		}()
		c.Next()
	}
}
